package org.spring.context.annotation;

import java.io.IOException;
import java.util.LinkedHashSet;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.spring.beans.BeanDefination;
import org.spring.beans.factory.BeanDefinationStoreException;
import org.spring.beans.factory.support.AnnotationBeanNameGenerator;
import org.spring.beans.factory.support.BeanDefinationRegistry;
import org.spring.beans.factory.support.BeanNameGenerator;
import org.spring.core.io.Resource;
import org.spring.core.io.support.PackageResourceLoader;
import org.spring.core.type.MetaDataReader;
import org.spring.core.type.classreading.SimpleMetaDataReader;
import org.spring.stereotype.Component;
import org.spring.util.StringUtils;

public class ClassPathBeanDefinationScanner {

	private final BeanDefinationRegistry registry;

	private PackageResourceLoader resourceLoader = new PackageResourceLoader();

	protected final Log logger = LogFactory.getLog(getClass());

	private BeanNameGenerator beanNameGenerator = new AnnotationBeanNameGenerator();

	public ClassPathBeanDefinationScanner(BeanDefinationRegistry registry) {
		this.registry = registry;
	}

	public Set<BeanDefination> doScan(String packagesToScan) {

		String[] basePackages = StringUtils.tokenizeToStringArray(packagesToScan, ",");

		Set<BeanDefination> BeanDefinations = new LinkedHashSet<BeanDefination>();
		for (String basePackage : basePackages) {
			Set<BeanDefination> candidates = findCandidateComponents(basePackage);
			for (BeanDefination candidate : candidates) {
				BeanDefinations.add(candidate);
				registry.registerDefination(candidate.getID(), candidate);

			}
		}
		return BeanDefinations;
	}

	public Set<BeanDefination> findCandidateComponents(String basePackage) {
		Set<BeanDefination> candidates = new LinkedHashSet<BeanDefination>();
		try {

			Resource[] resources = this.resourceLoader.getResources(basePackage);

			for (Resource resource : resources) {
				try {

					MetaDataReader metadataReader = new SimpleMetaDataReader(resource);

					if (metadataReader.getAnnotationMetadata().hasAnnotation(Component.class.getName())) {
						ScannedGenericBeanDefination sbd = new ScannedGenericBeanDefination(
								metadataReader.getAnnotationMetadata());
						String beanName = this.beanNameGenerator.generateBeanName(sbd, this.registry);
						sbd.setId(beanName);
						candidates.add(sbd);
					}
				} catch (Throwable ex) {
					throw new BeanDefinationStoreException("Failed to read candidate component class: " + resource, ex);
				}

			}
		} catch (IOException ex) {
			throw new BeanDefinationStoreException("I/O failure during classpath scanning", ex);
		}
		return candidates;
	}

}
